package com.hjt.gulimall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.alipay.api.AlipayApiException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hjt.common.exception.NoStockException;
import com.hjt.common.to.member.MemberAddressTo;
import com.hjt.common.to.member.UserLoginTo;
import com.hjt.common.to.mq.OrderTo;
import com.hjt.common.to.order.SkuLockTo;
import com.hjt.common.to.order.WareLockTo;
import com.hjt.common.to.product.SpuInfoTo;
import com.hjt.common.to.ware.SkuHasStockTo;
import com.hjt.common.utils.PageUtils;
import com.hjt.common.utils.Query;
import com.hjt.common.utils.R;
import com.hjt.gulimall.order.config.AlipayTemplate;
import com.hjt.gulimall.order.constant.OrderConstant;
import com.hjt.gulimall.order.constant.RabbitMqConstant;
import com.hjt.gulimall.order.dao.OrderDao;
import com.hjt.gulimall.order.entity.OrderEntity;
import com.hjt.gulimall.order.entity.OrderItemEntity;
import com.hjt.gulimall.order.entity.PaymentInfoEntity;
import com.hjt.gulimall.order.enume.OrderStatusEnum;
import com.hjt.gulimall.order.feign.CartFeignService;
import com.hjt.gulimall.order.feign.MemberFeignService;
import com.hjt.gulimall.order.feign.ProductFeignService;
import com.hjt.gulimall.order.feign.WareFeignService;
import com.hjt.gulimall.order.interceptor.UserLoginInterceptor;
import com.hjt.gulimall.order.service.OrderItemService;
import com.hjt.gulimall.order.service.OrderService;
import com.hjt.gulimall.order.service.PaymentInfoService;
import com.hjt.gulimall.order.to.OrderCreateTo;
import com.hjt.gulimall.order.vo.*;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    @Autowired
    private MemberFeignService memberFeignService;
    @Autowired
    private CartFeignService cartFeignService;

    @Autowired
    private WareFeignService wareFeignService;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private OrderItemService orderItemService;

    @Autowired
    private PaymentInfoService paymentInfoService;

    @Autowired
    private ThreadPoolExecutor executor;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private AlipayTemplate alipayTemplate;

    private ThreadLocal<OrderSubmitVo> orderSubmitVoThreadLocal = new ThreadLocal<>();
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    // 获取订单确认页信息
    @Override
    public OrderConfirmVo getOrderConfirmVo() throws ExecutionException, InterruptedException {

        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();

        // threadLoacl中获取登录用户信息
        UserLoginTo loginTo = UserLoginInterceptor.loginUser.get();
        Long memberId = loginTo.getId();

            // 获取当前请求的内容
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        // 1.用户地址信息
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes); // 给远程调用设置请求内容
            List<MemberAddressVo> memberAddressList = memberFeignService.getAddressListByMemberId(memberId);
            orderConfirmVo.setAddressList(memberAddressList);
        },executor);

        CompletableFuture<Void> itemFuture = CompletableFuture.runAsync(() -> {
            // 2.购物项信息
            // feignService在调用方法前会经过拦截器的拦截
            RequestContextHolder.setRequestAttributes(requestAttributes); // 给远程调用设置请求内容
            List<OrderItemVo> cartItemList = cartFeignService.getCurrentUserCartItemList();
            orderConfirmVo.setItemList(cartItemList);
        },executor).thenRunAsync(() -> {
            // 3. 商品库存信息
            List<Long> idList = orderConfirmVo.getItemList().stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());
            R response = wareFeignService.getSkuHasStock(idList);
            List<SkuHasStockTo> skuHasStockToList = response.getData(new TypeReference<List<SkuHasStockTo>>() {});
            //  封装成 {skuId:hasStock} 的map
            if (!CollectionUtils.isEmpty(skuHasStockToList)) {
                Map<Long, Boolean> hasStockMap = skuHasStockToList.stream().collect(Collectors.toMap(SkuHasStockTo::getSkuId, SkuHasStockTo::getHasStock));
                orderConfirmVo.setHasStockMap(hasStockMap);
            }
        }, executor);


        // 3.用户积分信息
        Integer integration = loginTo.getIntegration();
        orderConfirmVo.setIntegration(integration);


        // TODO:防重令牌
        String token = UUID.randomUUID().toString().replaceAll("-", "");
        orderConfirmVo.setToken(token); // 返回给前端
        redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX+loginTo.getId(), token, 30, TimeUnit.MINUTES); // 放到redis中
        CompletableFuture.allOf(addressFuture, itemFuture).get();

        return orderConfirmVo;
    }

    // 提交订单
    @Override
    @Transactional
    public OrderSubmitResponseVo submitOrder(OrderSubmitVo orderSubmitVo) {
        OrderSubmitResponseVo responseVo = new OrderSubmitResponseVo();
        // threadLoacl中获取登录用户信息
        UserLoginTo loginTo = UserLoginInterceptor.loginUser.get();
        Long memberId = loginTo.getId();
        //  创建订单，验证令牌、验价格、锁库存
        orderSubmitVoThreadLocal.set(orderSubmitVo); // 将提交的订单信息放到threadLocal中，后续多个方法调用可以使用
        // 1.验证令牌【令牌的对比与删除要保证原子性】,使用lua脚本
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Long result = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class),
                Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberId),
                orderSubmitVo.getOrderToken());

        if (result != null && result.equals(1L)) {
            // 令牌验证成功
            // 2.创建订单
            OrderCreateTo order = createOrder();
            // 验价
            BigDecimal orderPayPrice = order.getPayPrice();
            BigDecimal orderSubmitPayPrice = orderSubmitVo.getPayPrice();
            // 验价成功
            if (Math.abs(orderPayPrice.subtract(orderSubmitPayPrice).floatValue()) < 0.01) {
                saveOrder(order); // 保存订单
                // 锁定库存，有异常，回滚订单
                WareLockTo wareLockTo = new WareLockTo();
                wareLockTo.setOrderSn(order.getOrderEntity().getOrderSn());
                List<SkuLockTo> skuLockToList = order.getItems().stream().map(item -> {
                    SkuLockTo lockTo = new SkuLockTo();
                    lockTo.setNums(item.getSkuQuantity());
                    lockTo.setSkuId(item.getSkuId());
                    return lockTo;
                }).collect(Collectors.toList());
                wareLockTo.setItemList(skuLockToList);
                // 远程锁库存
                R r = wareFeignService.lockSku(wareLockTo);
                // 锁成功
                if (r.getCode() == 0) {
                    // TODO：订单发送消息给mq，自动关闭订单
                    rabbitTemplate.convertAndSend(RabbitMqConstant.ORDER_EVENT_EXCHANGE_NAME,RabbitMqConstant.ORDER_CREATE_ROUTE_KEY,order.getOrderEntity());
                    responseVo.setOrderEntity(order.getOrderEntity());
                    responseVo.setCode(0);
                } else {
                    // 锁定失败
                    responseVo.setCode(OrderConstant.WARE_LOCK_FAILED);
                    throw new NoStockException("库存不足");
                }
            } else {
                // 验证失败
                responseVo.setCode(OrderConstant.ORDER_PRICE_VERIFY_FAILED);
            }
        } else {
            // 令牌验证失败
            responseVo.setCode(OrderConstant.ORDER_TOKEN_VERIFY_FAILED);
        }
        return responseVo;
    }

    @Override
    public OrderEntity getByOrderSn(String orderSn) {
        return baseMapper.selectOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
    }

    // 关闭订单
    @Override
    public void closeOrder(OrderEntity order) {
        // 查询订单状态
        OrderEntity orderEntity = this.getById(order.getId());
        // 如果是待付款状态，则关闭订单
        if (orderEntity.getStatus().equals(com.hjt.common.constant.OrderConstant.ORDER_STATUS_WAIT_PAY)) {
            orderEntity.setStatus(com.hjt.common.constant.OrderConstant.ORDER_STATUS_CLOSED); // 更改为关闭状态
            baseMapper.updateById(orderEntity);
            // 订单释放成功，发送一个消息通知释放库存
            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(orderEntity, orderTo);
            try {
                // TODO：每个发送的消息都做好记录，定期扫描数据库，将失败的消息再发送一遍。
                rabbitTemplate.convertAndSend(RabbitMqConstant.ORDER_EVENT_EXCHANGE_NAME, RabbitMqConstant.ORDER_STOCK_ROUTE_KEY, orderTo);
            } catch (Exception e) {
                // TODO:消息发送失败后
            }
        }
    }

    @Override
    public String pay(String orderSn) throws AlipayApiException {
        OrderEntity orderEntity = getByOrderSn(orderSn);

        PayVo payVo = new PayVo();
        payVo.setOut_trade_no(orderSn);
        payVo.setTotal_amount(orderEntity.getPayAmount().setScale(2, BigDecimal.ROUND_UP).toString());
//        payVo.setSubject(orderEntity);
       return alipayTemplate.pay(payVo);
    }

    @Override
    public PageUtils pageWithItem(Map<String, Object> params) {
        Long memberId = UserLoginInterceptor.loginUser.get().getId();

        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>().eq("member_id", memberId).orderByDesc("id")
        );
        List<OrderEntity> orderEntityList = page.getRecords().stream().peek(orderEntity -> {
            List<OrderItemEntity> orderItemEntityList = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", orderEntity.getOrderSn()));
            orderEntity.setOrderItemList(orderItemEntityList);
        }).collect(Collectors.toList());

        page.setRecords(orderEntityList);

        return new PageUtils(page);
    }

    @Override
    public String handlePayResult(PayAsyncVo vo) {
        // 1.保存交易流水
        PaymentInfoEntity paymentInfoEntity = new PaymentInfoEntity();
        paymentInfoEntity.setOrderSn(vo.getOut_trade_no());
        paymentInfoEntity.setPaymentStatus(vo.getTrade_status());
        paymentInfoEntity.setTotalAmount(new BigDecimal(vo.getTotal_amount()));
        paymentInfoEntity.setCreateTime(new Date());
        paymentInfoEntity.setCallbackTime(vo.getNotify_time());
        paymentInfoService.save(paymentInfoEntity);

        // 2.修改订单的状态信息
        // 支付成功
        if (vo.getTrade_status().equals("TRADE_FINISHED") || vo.getTrade_status().equals("TRADE_SUCCESS")) {
            String orderSn = vo.getOut_trade_no();
            this.updateOrderStatus(orderSn, OrderStatusEnum.PAYED.getCode());
        }
        return "success";
    }

    private void updateOrderStatus(String orderSn, Integer code) {
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setStatus(code);
        baseMapper.update(orderEntity, new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
    }


    /**
     * 保存订单
     * @param order
     */
    private void saveOrder(OrderCreateTo order) {
        OrderEntity orderEntity = order.getOrderEntity();
        // 订单
        orderEntity.setCreateTime(new Date());
        orderEntity.setModifyTime(new Date());
        this.save(orderEntity);

        // 订单项
        for (OrderItemEntity item : order.getItems()) {
            orderItemService.save(item);
        }
    }


    private OrderCreateTo createOrder() {
        OrderCreateTo orderCreateTo = new OrderCreateTo();
        // 1.生成订单号,创建订单
        String orderSn = IdWorker.getTimeId(); // 使用mybatis-plus的方法
        OrderEntity orderEntity = buildOrder(orderSn);
        // 获取所有的订单项
        List<OrderItemEntity> orderItemEntities = buildOrderItemList(orderSn);
        // 计算订单价格相关
        countPrice(orderEntity, orderItemEntities);
        orderCreateTo.setOrderEntity(orderEntity);
        orderCreateTo.setItems(orderItemEntities);
        orderCreateTo.setPayPrice(orderEntity.getPayAmount());
        return orderCreateTo;
    }

    /**
     * 根据订单项计算订单的价格以及优惠信息
     * @param orderEntity
     * @param orderItemList
     */
    private void countPrice(OrderEntity orderEntity,List<OrderItemEntity> orderItemList) {
        BigDecimal totalAmount = new BigDecimal(0);
        BigDecimal promotionAmount = new BigDecimal(0);
        BigDecimal integrationAmount = new BigDecimal(0);
        BigDecimal couponAmount = new BigDecimal(0);
        for (OrderItemEntity itemEntity : orderItemList) {
            totalAmount = totalAmount.add(itemEntity.getRealAmount()); // 总价
            promotionAmount =  promotionAmount.add(itemEntity.getPromotionAmount()); // 折扣优惠价
            integrationAmount = integrationAmount.add(itemEntity.getIntegrationAmount()); // 积分优惠价格
            couponAmount = couponAmount.add(itemEntity.getCouponAmount()); // 优惠券优惠价格
        }
        BigDecimal payAmount = totalAmount.add(orderEntity.getFreightAmount()); // 实际支付价格（总价+运费）
        orderEntity.setTotalAmount(totalAmount);
        orderEntity.setPromotionAmount(promotionAmount);
        orderEntity.setIntegrationAmount(integrationAmount);
        orderEntity.setCouponAmount(couponAmount);
        orderEntity.setPayAmount(payAmount);

    }


    /**
     * 根据订单项vo封装订单项实体类
     * @param orderItemVo
     * @param orderSn 订单号
     * @return
     */
    private OrderItemEntity buildOrderItem(OrderItemVo orderItemVo, String orderSn) {
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        // 订单信息
        orderItemEntity.setOrderSn(orderSn);
        // spu信息
        Long skuId = orderItemVo.getSkuId();
        SpuInfoTo spuInfoTo = productFeignService.getSpuInfoBySkuId(skuId);
        orderItemEntity.setSpuId(spuInfoTo.getId());
        orderItemEntity.setSpuBrand(spuInfoTo.getBrandName());
        orderItemEntity.setSpuPic(spuInfoTo.getSpuDescription());
        orderItemEntity.setSpuName(spuInfoTo.getSpuName());
        // sku信息
        orderItemEntity.setSkuId(skuId);
        orderItemEntity.setSkuName(orderItemVo.getTitle());
        orderItemEntity.setSkuPic(orderItemVo.getImage());
        orderItemEntity.setSkuPrice(orderItemVo.getPrice());
        String attrString = StringUtils.collectionToDelimitedString(orderItemVo.getSkuAttr(), ";");// 销售属性组合
        orderItemEntity.setSkuAttrsVals(attrString);
        // 优惠信息
        // 积分信息
        orderItemEntity.setGiftGrowth(orderItemVo.getPrice().intValue());
        orderItemEntity.setGiftIntegration(orderItemVo.getPrice().intValue());
        // 订单项的价格信息
        orderItemEntity.setPromotionAmount(new BigDecimal(0)); // 促销
        orderItemEntity.setIntegrationAmount(new BigDecimal(0));// 积分优惠
        orderItemEntity.setCouponAmount(new BigDecimal(0));// 优惠券
        // 实际价格(单价*数量)
        orderItemEntity.setSkuQuantity(orderItemVo.getCount());
        BigDecimal totalPrice = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity()));
        BigDecimal realPrice = totalPrice.subtract(orderItemEntity.getCouponAmount())
                .subtract(orderItemEntity.getIntegrationAmount())
                .subtract(orderItemEntity.getPromotionAmount());
        orderItemEntity.setRealAmount(realPrice);
        return orderItemEntity;
    }

    /**
     * 构建订单项列表
     * @return
     */
    private List<OrderItemEntity> buildOrderItemList(String orderSn) {
        List<OrderItemVo> orderItemList = cartFeignService.getCurrentUserCartItemList();
        if (!CollectionUtils.isEmpty(orderItemList)) {
            return orderItemList.stream().map(cartItemVo -> buildOrderItem(cartItemVo, orderSn))
                                       .collect(Collectors.toList());
        }
        return null;
    }

    /**
     * 根据给定的订单号创建一个订单
     * 封装ThreadLocal中的OrderSubmitVo数据
     * @param orderSn
     * @return
     */
    private OrderEntity buildOrder(String orderSn) {
        OrderEntity orderEntity = new OrderEntity();
        OrderSubmitVo orderSubmitVo = orderSubmitVoThreadLocal.get();
        orderEntity.setOrderSn(orderSn);
        Long addrId = orderSubmitVo.getAddrId();
        // 1.会员信息
        Long memberId = UserLoginInterceptor.loginUser.get().getId();
        orderEntity.setMemberId(memberId);
        // 2.运费信息
        BigDecimal fare =  wareFeignService.getFare(addrId).getData(new TypeReference<BigDecimal>(){});
        orderEntity.setFreightAmount(fare);
        // 3.1 地址信息
        MemberAddressTo memberAddressTo = memberFeignService.getAddressByAddressId(addrId).getData(new TypeReference<MemberAddressTo>(){});
        orderEntity.setReceiverProvince(memberAddressTo.getProvince());
        orderEntity.setReceiverCity(memberAddressTo.getCity());
        orderEntity.setReceiverRegion(memberAddressTo.getRegion());
        orderEntity.setReceiverPostCode(memberAddressTo.getPostCode());
        orderEntity.setReceiverDetailAddress(memberAddressTo.getDetailAddress());
        // 3.2 收货人信息
        orderEntity.setReceiverName(memberAddressTo.getName());
        orderEntity.setReceiverPhone(memberAddressTo.getPhone());

        // 4.订单状态
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setAutoConfirmDay(OrderConstant.AUTO_CONFIRM_DAY);
        orderEntity.setDeleteStatus(0);
        return orderEntity;
    }
}